using UnityEngine;
using System.Collections.Generic;
using AreaGenerator;
using Entities;

public class GenerateLevel : MonoBehaviour {
	
	//prefabs
	public GameObject tile_default;
	public GameObject tile_depth;
	public GameObject tile_high;
	public GameObject tile_high_depth;
	
	public GameObject no_wall_default;
	public GameObject no_wall_depth;
	public GameObject no_wall_high;
	public GameObject no_wall_high_depth;
	
	public GameObject doorway;
	public GameObject doorway_high;
	public GameObject stairs;
	public GameObject stairs_bridge;
	public GameObject stairs_to_depth;
	public GameObject stairs_from_depth;
	public GameObject bridge;
	public GameObject bridge_high;
	public GameObject bridge_doorway;
	public GameObject bridge_doorway_high;
	
	public GameObject wall_default;
	public GameObject edge;
	public GameObject wall_depth;
	public GameObject wall_depth_high;
	
	public GameObject exit;
	public GameObject entrance;
	
	public GameObject pillar;
	public GameObject pillar_extension;
	public GameObject pillar_extension_high;
	
	private GameObject areaObj;
	
	private static int TILESIZE = 20;
	private static int WALLSIZE = 3;
	private static int PILLARSIZE = 5;
	private static int WALLHEIGHTFACTOR = 2;
	
	private static Dictionary<WallType, GameObject> wallTypesMap;
	
	// Use this for initialization
	void Start () {
		
		wallTypesMap = new Dictionary<WallType, GameObject>();
		wallTypesMap[WallType.doorway] = doorway;
		wallTypesMap[WallType.doorway_high] = doorway_high;
		wallTypesMap[WallType.stairs] = stairs;
		wallTypesMap[WallType.stairs_bridge] = stairs_bridge;
		wallTypesMap[WallType.stairs_to_depth] = stairs_to_depth;
		wallTypesMap[WallType.stairs_from_depth] = stairs_from_depth;
		wallTypesMap[WallType.bridge] = bridge;
		wallTypesMap[WallType.bridge_high] = bridge_high;
		wallTypesMap[WallType.bridge_doorway] = bridge_doorway;
		wallTypesMap[WallType.bridge_doorway_high] = bridge_doorway_high;
		wallTypesMap[WallType.wall_default] = wall_default;
		wallTypesMap[WallType.edge] = edge;
		wallTypesMap[WallType.wall_depth] = wall_depth;
		wallTypesMap[WallType.wall_depth_high] = wall_depth_high;
		
		areaObj = GameObject.FindGameObjectWithTag("Area");
		
		int width = 10;
		int length = 15;
		Area area = new Area(width,length);
		
		area.removeRandomWalls(120);
		
		area.setEntrance(1,1);
		area.setExit(width-3,length-3);
		
		/*foreach (Room r in area.getRooms()){
			print(r.getTilesString());
			print("adjacents : " + r.getAdjacentRooms().Count);
			foreach (Room a in r.getAdjacentRooms()){
				print(a.getTilesString());
			}
			print("====================");
		}*/
		
		area.setGround();
		
		area.setDoors();
		
		area.setWallTypes();
		
		area.setDoorTypes();
		
		GameObject obj;
		RoomTile entranceTile = null;
		foreach (Room room in area.getRooms()){
			if (room.isEnabled()){
		    	foreach (RoomTile tile in room.getTiles()){
					drawTile(tile, room);
					
					obj = entrance;
					if (tile.isEntrance()){
						instantiateInArea(obj, new Vector3(tile.sw.x * (TILESIZE+WALLSIZE) , obj.transform.position.y, tile.sw.y * (TILESIZE+WALLSIZE)), new Quaternion());
						entranceTile = tile;
					}
					
					obj = exit;
					if (tile.isExit()){
						instantiateInArea(obj, new Vector3(tile.sw.x * (TILESIZE+WALLSIZE) , obj.transform.position.y, tile.sw.y * (TILESIZE+WALLSIZE)), new Quaternion());
					}
				}
				
				foreach (Wall nowall in room.getNoWalls()){
		    		drawNoWall(nowall, room);
		    	}
			}
		}
		
		foreach (Wall wall in area.getWalls()){
    		drawWall(wall);
    	}
	    
    	foreach (Wall door in area.getDoors()){
    		drawWall(door);
    	}
		
		foreach (Node node in area.getEnabledNodes()){
			if(!area.isDeepNode(node)) drawPillar(node, area);
    	}
		
		
		Player player = new Player();
		obj = (GameObject)Resources.Load("Player");
		player.setGameObject(instantiateInArea(obj, new Vector3(entranceTile.sw.x * (TILESIZE+WALLSIZE) + 3, obj.transform.position.y + 10, entranceTile.sw.y * (TILESIZE+WALLSIZE) + 3), new Quaternion()));
		Players.Instance.addPlayer(player);
		
		instanciateMobGroups(area);
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	private GameObject instantiateInArea(GameObject type, Vector3 pos, Quaternion rot){
		GameObject instance = Instantiate(type, pos, rot) as GameObject;
		instance.transform.parent = areaObj.transform;
		return instance;
	}
	
	private void drawWall(Wall wall){
		Vector3 pos = new Vector3((wall.n1.x + wall.n2.x) / 2, 0, (wall.n1.y + wall.n2.y) / 2);
		if (wall.n1.x == wall.n2.x) pos.x -= 0.5f;
		else pos.z -= 0.5f;
		Vector3 dir = new Vector3(wall.n2.x - wall.n1.x, 0, wall.n2.y - wall.n1.y);
		if (wall.mirrored) dir = -dir;
		Quaternion rot = new Quaternion();
		rot.SetLookRotation(dir);
		if (wall.type != WallType.depth){
			GameObject obj = wallTypesMap[wall.type];
			GameObject instance = instantiateInArea(obj, new Vector3(pos.x * (TILESIZE+WALLSIZE), obj.transform.position.y, pos.z * (TILESIZE+WALLSIZE)), rot);
			float xScale = instance.transform.localScale.x * WALLSIZE;
			float yScale = instance.transform.localScale.y;
			float zScale = instance.transform.localScale.z * TILESIZE;
			instance.transform.localScale = new Vector3(xScale, yScale, zScale);
			if (instance.transform.GetChildCount() == 0) {
				scaleMain(instance.transform);
			} else {
				foreach (Transform t in instance.transform.GetComponentsInChildren<Transform>()){
					if(t.gameObject.tag == "Extension"){
						scaleExtension(t);
					} else if (t.gameObject.tag == "Main") scaleMain(t);
				}
			}
		}
	}
	
	
	private void drawNoWall(Wall wall, Room room){
		Vector3 pos = new Vector3((wall.n1.x + wall.n2.x) / 2, 0, (wall.n1.y + wall.n2.y) / 2);
		if (wall.n1.x == wall.n2.x) pos.x -= 0.5f;
		else pos.z -= 0.5f;
		Vector3 dir = new Vector3(wall.n2.x - wall.n1.x, 0, wall.n2.y - wall.n1.y);
		Quaternion rot = new Quaternion();
		rot.SetLookRotation(dir);
		
		GameObject obj = no_wall_default;
		if (room.isDeep()){
			if (room.isHigh()) obj = no_wall_high_depth;
			else obj = no_wall_depth;
		} else if (room.isHigh() && !room.isDeep()) obj = no_wall_high;
		
		GameObject instance = instantiateInArea(obj, new Vector3(pos.x * (TILESIZE+WALLSIZE), obj.transform.position.y, pos.z * (TILESIZE+WALLSIZE)), rot);
		float xScale = instance.transform.localScale.x * WALLSIZE;
		float yScale = instance.transform.localScale.y;
		float zScale = instance.transform.localScale.z * TILESIZE;
		instance.transform.localScale = new Vector3(xScale, yScale, zScale);
		if (room.isDeep()){
			foreach (Transform t in instance.transform.GetComponentsInChildren<Transform>()){
				if(t.gameObject.tag == "Extension"){
					scaleExtension(t);
				}
			}
		}	
	}
	
	private void drawTile(RoomTile tile, Room room){
		GameObject obj = tile_default;
		if (room.isDeep()){
			if (room.isHigh()) obj = tile_high_depth;
			else obj = tile_depth;
		} else if (room.isHigh() && !room.isDeep()) obj = tile_high;
		GameObject instance = instantiateInArea(obj, new Vector3(tile.sw.x * (TILESIZE+WALLSIZE) , obj.transform.position.y, tile.sw.y * (TILESIZE+WALLSIZE)), new Quaternion());
		float xScale = instance.transform.localScale.x * TILESIZE;
		float yScale = instance.transform.localScale.y;
		float zScale = instance.transform.localScale.z * TILESIZE;	
		instance.transform.localScale = new Vector3(xScale, yScale, zScale);
	}
	
	private void drawPillar(Node node, Area area){
		GameObject obj = pillar;
		GameObject instance = instantiateInArea(obj, new Vector3((node.x - 0.5f) * (TILESIZE+WALLSIZE) , obj.transform.position.y, (node.y - 0.5f) * (TILESIZE+WALLSIZE)), new Quaternion());
		float xScale = instance.transform.localScale.x * PILLARSIZE;
		float yScale = instance.transform.localScale.y;
		float zScale = instance.transform.localScale.z * PILLARSIZE;
		instance.transform.localScale = new Vector3(xScale, yScale, zScale);
		
		RoomTile neTile = area.getTile(node, diagOrientation.ne);
		RoomTile seTile = area.getTile(node, diagOrientation.se);
		RoomTile swTile = area.getTile(node, diagOrientation.sw);
		RoomTile nwTile = area.getTile(node, diagOrientation.nw);
		if (neTile != null && seTile != null && swTile != null && nwTile != null){
		Room[] oriRooms = new Room[4];
			oriRooms[0] = neTile.getRoom();
			oriRooms[1] = seTile.getRoom();
			oriRooms[2] = swTile.getRoom();
			oriRooms[3] = nwTile.getRoom();
			
			Wall[] oriWalls = new Wall[4];
			Wall eastWall = area.getWall(node, node.east);
			Wall southWall = area.getWall(node, node.south);
			Wall westWall = area.getWall(node, node.west);
			Wall northWall = area.getWall(node, node.north);
			oriWalls[0] = eastWall;
			oriWalls[1] = southWall;
			oriWalls[2] = westWall;
			oriWalls[3] = northWall;
		
			for (int ori = 0 ; ori < 4 ; ori++){
				if (oriRooms[ori].isDeep() && oriRooms[ori].isEnabled()){
					if(oriWalls[(ori+3)%4] != null &&
						oriWalls[ori] != null &&
						!oriWalls[(ori+3)%4].isDoor() && 
						!oriWalls[ori].isDoor() && 
						oriWalls[(ori+3)%4].type != WallType.depth && 
						oriWalls[ori].type != WallType.depth)
					drawPillarExtension(instance, (diagOrientation)ori, oriRooms[ori].isHigh());
				}
			}
		}
	}
	
	private void drawPillarExtension(GameObject pillar, diagOrientation ori, bool high){
		GameObject obj = high ? pillar_extension_high : pillar_extension;
		GameObject instance = instantiateInArea(obj, new Vector3(0,obj.transform.position.y * pillar.transform.localScale.y,0), new Quaternion());
		instance.transform.parent = pillar.transform;
		float xsign = 1;
		float zsign = 1;
		switch(ori) {
			case diagOrientation.ne :
				break;
			case diagOrientation.se :
				zsign = -1;
				break;
			case diagOrientation.sw :
				xsign = -1;
				zsign = -1;
				break;
			case diagOrientation.nw :
				xsign = -1;
				break;
		}
		float xPos = xsign * ((TILESIZE*0.2f)+(float)WALLSIZE/2)/PILLARSIZE/2;
		float yPos = instance.transform.localPosition.y;
		float zPos = zsign * ((TILESIZE*0.2f)+(float)WALLSIZE/2)/PILLARSIZE/2;
		instance.transform.localPosition = new Vector3(xPos, yPos, zPos);

		float xScale = (TILESIZE*0.2f+(float)WALLSIZE/2)/PILLARSIZE;
		float yScale = instance.transform.localScale.y;
		float zScale = (TILESIZE*0.2f+(float)WALLSIZE/2)/PILLARSIZE;
		instance.transform.localScale = new Vector3(xScale, yScale, zScale);
	}
	
	private void scaleExtension(Transform extension){
		float xPos = ((TILESIZE*0.1f)+(float)WALLSIZE/2)/WALLSIZE;
		float yPos = extension.localPosition.y;
		float zPos = extension.localPosition.z;
		if (extension.localPosition.x > 0){
			extension.localPosition = new Vector3(xPos, yPos, zPos);
		} else {
			extension.localPosition = new Vector3(-xPos, yPos, zPos);
		}
		float xScale = extension.localScale.x * ((float)TILESIZE/(float)WALLSIZE);
		float yScale = extension.localScale.y;
		float zScale = extension.localScale.z ;
		extension.localScale = new Vector3(xScale,yScale,zScale);
	}

	private void scaleMain(Transform main){
		float xScale = main.localScale.x;
		float yScale = main.localScale.y;
		float zScale = main.localScale.z * (TILESIZE + WALLSIZE - PILLARSIZE)/TILESIZE;
		main.localScale = new Vector3(xScale,yScale,zScale);
	}
	
	private void instanciateMobGroups(Area area){
		foreach(Room room in area.getRooms()){
			if (room.isEnabled()){
				foreach(RoomTile tile in room.getTiles()){
					if (area.random.Next(10) > 4) {
						MobGroup mobGroup = new MobGroup(area.random.Next(10), area.random.Next(10), area.random.Next());
						mobGroup.instanciateMobs<Zombie>(1);//area level
						GameObject obj = (GameObject)Resources.Load("mobGroup");
						GameObject instance = instantiateInArea(obj, new Vector3(tile.sw.x * (TILESIZE+WALLSIZE) , obj.transform.position.y + 5, tile.sw.y * (TILESIZE+WALLSIZE)), new Quaternion());
						SpawnMobGroup script = (SpawnMobGroup) instance.GetComponent(typeof(SpawnMobGroup));
						script.mobGroup = mobGroup;
						mobGroup.setStartingPosition(script.gameObject.transform.position);
					}
			}
			}
		}
	}
}
